/*
fxp.h: Main include file for the Fudepan XML Parser project.
    Copyright (C) 2009, 2010  Daniel Gutson, FuDePAN

    This file is part of the FXP project.

    FXP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    FXP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FXP.  If not, see <http://www.gnu.org/licenses/>.

    This is the main include file.
*/

#ifndef FXP_H
#define FXP_H

#include <string>
#include <map>

#include <mili/mili.h>

using std::string;

namespace fxp
{
using namespace mili;

struct FXPListener
{
    enum AttributeOutput
    {
        AttributeAccepted,
        MalformedAttributeValue,
        UnknownAttribute
    };

    virtual AttributeOutput new_attribute(const string& name, const string& value) = 0;
    virtual FXPListener* new_child(const string& name) = 0;  /* NULL if not accepted */
    virtual bool receive_text(const string& text) = 0;
    virtual void close() {}
    virtual ~FXPListener(){}
};

struct FXPDumbListener : FXPListener
{
    inline virtual AttributeOutput new_attribute(const string& /*name*/, const string& /*value*/);
    inline virtual FXPListener* new_child(const string& /*name*/);
    inline virtual bool receive_text(const string& /*text*/);
};

enum ErrorAction
{
    Ignore,
    Warn,
    Stop
};

enum TextAction
{
    DisableTextProcessing,
    IgnoreUnacceptedText,
    WarnOnUnacceptedText,
    StopOnUnacceptedText
};

struct ParseErrorData
{
    size_t line;
    string error;

    ParseErrorData()
        : line(0), error()
    {}
};

template <class T> class FXPRootParser;
template <class Container> class FXPRootParserNonDef;

//--------------------------

template <class T>
struct AttributeSetter
{
    virtual bool set_value(T& t, const string& value) = 0;
    virtual ~AttributeSetter(){}
};

template <class T>
struct ParentChildBinder
{
    virtual FXPListener* pass_instance(T& t) = 0;
    virtual ~ParentChildBinder() {}
};

template <class T>
class FXParser : protected FXPListener
{
    const std::string id;
    T* instance;

    std::map<std::string, AttributeSetter<T>*> attr_setters;
    std::map<std::string, ParentChildBinder<T>*> children_binders;
    AttributeSetter<T>* text_setter;
protected:
    inline bool acceptable_attr(const string& attr) const;

    inline bool accepts_text() const;

    inline bool instance_set() const;

    inline T* get_instance() const;

    inline void reset_instance();

public:
    FXParser(const std::string& id)
        : id(id), instance(NULL), text_setter(NULL)
    {}

    inline void add_attr(const string& attr, AttributeSetter<T>* attr_setter);

    inline void add_child(const string& collection_name, ParentChildBinder<T>* pcb);

    inline void set_text_attr(AttributeSetter<T>* setter);

    inline FXPListener* get_listener(T& t);

    inline const std::string& get_id() const;

    inline ~FXParser();

protected:
    /* FXPListener interface implementation */
    inline virtual AttributeOutput new_attribute(const string& name, const string& value);

    inline virtual FXPListener* new_child(const string& name);

    inline virtual bool receive_text(const string& text);
};

// Non-default implementation:

template <class T, class Constructor>
class FXParserNonDef : public FXParser<T>
{
    ContainerAdapter<T>* containerAdapter;
    std::list<std::pair<string, string> > pending_attrs;
    string pending_text;
public:
    Constructor constructor;

    FXParserNonDef(const std::string& id)
        : FXParser<T>::FXParser(id), containerAdapter(NULL)
    {}

    inline ~FXParserNonDef();

    template <class ContainerType>
    inline FXPListener* get_listener(ContainerType& cont);

private:
    /* FXPListener interface implementation */
    inline virtual FXPListener::AttributeOutput new_attribute(const string& name, const string& value);

    inline void create_element();

    inline virtual FXPListener* new_child(const string& name);

    inline virtual bool receive_text(const string& text);

    inline virtual void close();
};

// Constructors & Parameters:

template <class T, class T0>
class Constructor1
{
    string attr_name;
protected:
    T0 param0;
    T0 default_value;
public:
    inline void set_param0(const string& attr);

    inline void set_param0(const string& attr, const T0& default_value);

    inline void set_param0_constant(const T0& constant_value);

    inline bool accepts(const string& attr, const string& value);

    inline T* create();

    inline void reset();

};

template <class T0>
class Parameters1
{
public:
    typedef map_type(BasicType, T0) BType;
private:
    string attr_name;
protected:
    BType param0;
    BType default_value;
public:

    template <class Parent, class RetType>
    struct Method_type
    {
        typedef RetType(Parent::*Type)(T0);
    };

    inline void set_param0(const string& attr);

    inline void set_param0(const string& attr, const BType& default_value);

    inline void set_param0_constant(const BType& constant_value);

    inline bool accepts(const string& attr, const string& value);

    template <class T, class M>
    inline void invoke(T* instance, M method);

    inline void reset();
};

#define SET_PARAM_CONSTANT_NAME(previous, suffix) set_param##previous##suffix

#define DECLARE_CONSTRUCTOR(current, previous)                                              \
class Constructor##current : public Constructor##previous<T, BASE_TYPES>                    \
{                                                                                           \
    string attr_name;                                                                       \
protected:                                                                                  \
    T##previous param##previous;                                                            \
    T##previous default_value;                                                              \
public:                                                                                     \
    void set_param##previous(const string& attr)                                            \
    {                                                                                       \
        attr_name = attr;                                                                   \
    }                                                                                       \
                                                                                            \
    void set_param##previous(const string& attr, const T##previous& default_value)          \
    {                                                                                       \
        attr_name = attr;                                                                   \
        this->default_value = param##previous = default_value;                              \
    }                                                                                       \
                                                                                            \
    void SET_PARAM_CONSTANT_NAME(previous,_constant)(const T##previous& constant_value)     \
    {                                                                                       \
        attr_name.clear();                                                                  \
        default_value = param##previous = constant_value;                                   \
    }                                                                                       \
                                                                                            \
    bool accepts(const string& attr, const string& value)                                   \
    {                                                                                       \
        if (attr == attr_name)                                                              \
            return from_string(value, param##previous);                                     \
        else                                                                                \
            return Constructor##previous<T, BASE_TYPES>::accepts(attr, value);              \
    }                                                                                       \
                                                                                            \
    T* create()                                                                             \
    {                                                                                       \
        return new T(BASE_PARAMETERS, param##previous);                                     \
    }                                                                                       \
                                                                                            \
    void reset()                                                                            \
    {                                                                                       \
        param##previous = default_value;                                                    \
        Constructor##previous<T, BASE_TYPES>::reset();                                      \
    }                                                                                       \
}

#define DECLARE_PARAMETERS(current, previous)                                               \
class Parameters##current : public Parameters##previous<BASE_TYPES>                         \
{                                                                                           \
public:                                                                                     \
    typedef map_type(BasicType, T##previous) BType;                                         \
private:                                                                                    \
    string attr_name;                                                                       \
protected:                                                                                  \
    BType param##previous;                                                                  \
    BType default_value;                                                                    \
public:                                                                                     \
    template <class Parent, class RetType>                                                  \
    struct Method_type                                                                      \
    {                                                                                       \
        typedef RetType (Parent::*Type)(BASE_TYPES, T##previous);                           \
    };                                                                                      \
                                                                                            \
    void set_param##previous(const string& attr)                                            \
    {                                                                                       \
        attr_name = attr;                                                                   \
    }                                                                                       \
                                                                                            \
    void set_param##previous(const string& attr, const BType& default_value)                \
    {                                                                                       \
        attr_name = attr;                                                                   \
        this->default_value = param##previous = default_value;                              \
    }                                                                                       \
                                                                                            \
    void SET_PARAM_CONSTANT_NAME(previous,_constant)(const BType& constant_value)           \
    {                                                                                       \
        attr_name.clear();                                                                  \
        default_value = param##previous = constant_value;                                   \
    }                                                                                       \
                                                                                            \
    bool accepts(const string& attr, const string& value)                                   \
    {                                                                                       \
        if (attr == attr_name)                                                              \
            return from_string(value, param##previous);                                     \
        else                                                                                \
            return Parameters##previous<BASE_TYPES>::accepts(attr, value);                  \
    }                                                                                       \
                                                                                            \
    template <class T, class M>                                                             \
    void invoke(T* instance, M method)                                                      \
    {                                                                                       \
        (instance->*method)(BASE_PARAMETERS, param##previous);                              \
    }                                                                                       \
                                                                                            \
    void reset()                                                                            \
    {                                                                                       \
        param##previous = default_value;                                                    \
        Parameters##previous<BASE_TYPES>::reset();                                          \
    }                                                                                       \
}

#define BASE_TYPES      T0
#define BASE_PARAMETERS this->param0
template <class T, class T0, class T1>
DECLARE_CONSTRUCTOR(2, 1);
template <class T0, class T1>
DECLARE_PARAMETERS(2, 1);
#undef BASE_TYPES
#undef BASE_PARAMETERS

#define BASE_TYPES      T0, T1
#define BASE_PARAMETERS this->param0, this->param1
template <class T, class T0, class T1, class T2>
DECLARE_CONSTRUCTOR(3, 2);
template <class T0, class T1, class T2>
DECLARE_PARAMETERS(3, 2);
#undef BASE_TYPES
#undef BASE_PARAMETERS

#define BASE_TYPES      T0, T1, T2
#define BASE_PARAMETERS this->param0, this->param1, this->param2
template <class T, class T0, class T1, class T2, class T3>
DECLARE_CONSTRUCTOR(4, 3);
template <class T0, class T1, class T2, class T3>
DECLARE_PARAMETERS(4, 3);
#undef BASE_TYPES
#undef BASE_PARAMETERS

#define BASE_TYPES      T0, T1, T2, T3
#define BASE_PARAMETERS this->param0, this->param1, this->param2, this->param3
template <class T, class T0, class T1, class T2, class T3, class T4>
DECLARE_CONSTRUCTOR(5, 4);
template <class T0, class T1, class T2, class T3, class T4>
DECLARE_PARAMETERS(5, 4);
#undef BASE_TYPES
#undef BASE_PARAMETERS

/* Add more here if needed. (you shouldn't create a constructor with more than 5 parameters!) */

#undef SET_PARAM_CONSTANT_NAME
#undef DECLARE_CONSTRUCTOR

// Implementations -----------------------------------------------------------------

// 1) Attributes
template <class T, class AttrType>
class AttributeSetterImpl : public AttributeSetter<T>
{
    AttrType(T::*attr);
    inline virtual bool set_value(T& t, const string& value);

public:
    AttributeSetterImpl(AttrType(T::*attr)) : attr(attr) {}
};


// 1.1) Attributes set with a method:
template <class T, class AttrType, class RetType> /* RetType doesn't matter */
class AttributeSetterMethodImpl : public AttributeSetter<T>
{
    RetType(T::*method)(AttrType);
    inline virtual bool set_value(T& t, const string& value);

public:
    AttributeSetterMethodImpl(RetType(T::*m)(AttrType)) : method(m) {}
};

// 1.2) Ignorable attributes
template <class T>
class IgnoreAttributeSetter : public AttributeSetter<T>
{
    inline virtual bool set_value(T& t, const string& value);
};

// 2) Containers

template <class T, class ContainerType, class ElementType> /* Add Insertion Policy here */
class ParentChildBinderImpl : public ParentChildBinder<T>
{
    ContainerType(T::*container);
    FXParser<ElementType>& child_parser;

    inline virtual FXPListener* pass_instance(T& t);

public:
    ParentChildBinderImpl(ContainerType(T::*container), FXParser<ElementType>& child_parser)
        : container(container), child_parser(child_parser)
    {}
};

// 2.1) NonDefault version

template <class T, class ContainerType, class ElementType, class Constructor>
class ParentChildNonDefBinderImpl : public ParentChildBinder<T>
{
    ContainerType(T::*container);
    FXParserNonDef<ElementType, Constructor>& child_parser;

    inline virtual FXPListener* pass_instance(T& t);

public:
    ParentChildNonDefBinderImpl(ContainerType(T::*container), FXParserNonDef<ElementType, Constructor>& child_parser)
        : container(container), child_parser(child_parser)
    {}
};

// 3) LOVs

class IgnoringParser : protected FXPListener
{
    const std::string id;
    std::set<string> ignores;
protected:
    inline bool ignorable(const string& attr_name) const;
public:
    IgnoringParser(const std::string& id)
        : id(id)
    {}

    inline void ignore_attr(const string& attr_name);

    inline const std::string& get_id() const;
};

template <class Container, class ElementType>
class FXParserLOV : public IgnoringParser
{
    Container* c;
    const std::string xml_field;

public:
    FXParserLOV(const string& id, const string& xml_field)
        : IgnoringParser(id), xml_field(xml_field)
    {}

    inline FXPListener* get_listener(Container& cont);

private:
    /* FXPListener implementation*/
    inline virtual AttributeOutput new_attribute(const string& name, const string& value);

    inline virtual FXPListener* new_child(const string& /*name*/);

    inline virtual bool receive_text(const string& /*text*/);
};

template <class T, class ContainerType, class ElementType>
class ParentLovBinderImpl : public ParentChildBinder<T>
{
    ContainerType(T::*container);
    FXParserLOV<ContainerType, ElementType>* const lov_parser;
    const bool delete_on_destructor;

    virtual FXPListener* pass_instance(T& t);

public:
    ParentLovBinderImpl(ContainerType(T::*container), FXParserLOV<ContainerType, ElementType>& lov_parser)
        : container(container), lov_parser(&lov_parser), delete_on_destructor(false)
    {}

    ParentLovBinderImpl(ContainerType(T::*container), FXParserLOV<ContainerType, ElementType>* lov_parser)
        : container(container), lov_parser(lov_parser), delete_on_destructor(true)
    {}

    inline ~ParentLovBinderImpl();

};

#if 0
template <class Container, class ElementType>
inline void ignore_attr(FXParserLOV<Container, ElementType>& parser, const string& attr_name)
{
    parser.ignore_attr(attr_name);
}
#endif

// 3.1) LOVs for methods

template <class T, class RetType, class Parameters>
class FXParserLOVMethod : public IgnoringParser
{
    T* instance;

    typename Parameters::template Method_type<T, RetType>::Type method;

public:
    Parameters parameters;

    FXParserLOVMethod(const string& id, typename Parameters::template Method_type<T, RetType>::Type method)
        : IgnoringParser(id), method(method)
    {}

    inline FXPListener* get_listener(T& t);

private:
    /* FXPListener implementation*/
    inline virtual FXPListener::AttributeOutput new_attribute(const string& name, const string& value);

    inline virtual FXPListener* new_child(const string& /*name*/);

    inline virtual bool receive_text(const string& /*text*/);

    inline virtual void close();
};

// this could be optimized to save space avoiding code bloat.
template <class T, class ChildParserType>
struct ParentChildDummyBinder : ParentChildBinder<T>
{
    ChildParserType& child_parser;

    ParentChildDummyBinder(ChildParserType& child_parser)
        : child_parser(child_parser)
    {}

    inline virtual FXPListener* pass_instance(T& t);
};


// 4) STL

template <template <class, class> class C, class T, class A >
class FXParser<C<T, A> > : public IgnoringParser
{
    C<T, A>* instance;      /* TODO DFG: Should use MiLi's ContainerAdapter */
//    const string element_name;
    FXParser<T>& child_parser;
public:
    FXParser(/*const string& element_name, */FXParser<T>& child_parser)
        : IgnoringParser(/*element_name*/ child_parser.get_id()),
          //element_name(element_name),
          child_parser(child_parser)
    {}

    inline FXPListener* get_listener(C<T, A>& t);

private:
    /* FXPListener interface implementation */
    inline virtual AttributeOutput new_attribute(const string& name, const string& /*value*/);

    inline virtual FXPListener* new_child(const string& name);

    inline virtual bool receive_text(const string& /*text*/);
};

/* 5) Root Parsers: two kinds, with subtypes:
    - One for structures (that behaves as a normal parser)
    - One for freestanding STL containers
        - with default-constructible members
        - with non-default construtible members

*/

class FXPRootListener : public FXPListener
{
    const string root_name;
    FXPListener* first;

    inline virtual AttributeOutput new_attribute(const string& /*name*/, const string& /*value*/);

    inline virtual FXPListener* new_child(const string& name);

    inline virtual bool receive_text(const string& /*text*/);

public:
    FXPRootListener(const string& root_name)
        : root_name(root_name)
    {}

    inline void set_first_listener(FXPListener* first_listener);
};

// Type 1: for normal structures.
//    DFG TODO TEST!!!!!!!!!
template <class T>
class FXPRootParser : public FXParser<T>
{
    FXPRootListener root_listener;
public:
    FXPRootParser(const std::string& id)
        : FXParser<T>(id), root_listener(id)
    {}

    inline FXPListener* get_listener(T& t);
};


// Type 2: for freestanding containers.
//  2.1) Default-constructible members

template <template <class, class> class C, class T, class A >
class FXPRootParser<C<T, A> > : public FXParser<C<T, A> >
{
    FXPRootListener root_listener;
public:
    FXPRootParser(const std::string& id, FXParser<T>& child_parser)
        : FXParser<C<T, A> >(/*child_parser.get_id(), */child_parser), root_listener(id)
    {}

    inline FXPListener* get_listener(C<T, A>& t);
};

// 2.2) Non-default constructible members. Supports sets.
template <class Container>
class FXPRootParserNonDef : public IgnoringParser
{
    FXPRootListener root_listener;
    //FXParserNonDef<T, Constructor>& child_parser;
    ParentChildBinder<Container>* const binder;
    Container* instance;
    const string child_id;

    inline virtual AttributeOutput new_attribute(const string& name, const string& /*value*/);

    inline virtual FXPListener* new_child(const string& name);

    inline virtual bool receive_text(const string& /*text*/);

    template <class Constructor>
    struct _ParentChildBinderImpl : public ParentChildBinder<Container>
    {
        FXParserNonDef< typename Container::value_type, Constructor>& child_parser;

        inline virtual FXPListener* pass_instance(Container& c);

        _ParentChildBinderImpl(FXParserNonDef<typename Container::value_type, Constructor>& child_parser)
            : child_parser(child_parser)
        {}
    };
public:

    template <class Constructor>
    FXPRootParserNonDef(const std::string& id, FXParserNonDef< typename Container::value_type, Constructor>& child_parser)
        : IgnoringParser(id),
          root_listener(id),
          binder(new _ParentChildBinderImpl<Constructor>(child_parser)),
          child_id(child_parser.get_id())
    {}

    inline FXPListener* get_listener(Container& t);

    inline ~FXPRootParserNonDef();

};

}

#define FXP_INLINE_H
#include "fxp_inline.h"
#undef FXP_INLINE_H
#endif
